home *** CD-ROM | disk | FTP | other *** search
/ Aminet 35 / Aminet 35 (2000)(Schatztruhe)[!][Feb 2000].iso / Aminet / game / shoot / ADescentSrc.lha / descent / main / playsave.c < prev    next >
C/C++ Source or Header  |  1998-03-03  |  21KB  |  751 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: /usr/CVS/descent/main/playsave.c,v $
  15.  * $Revision: 1.1.1.1 $
  16.  * $Author: nobody $
  17.  * $Date: 1998/03/03 15:12:28 $
  18.  * 
  19.  * Functions to load & save player games
  20.  * 
  21.  * $Log: playsave.c,v $
  22.  * Revision 1.1.1.1  1998/03/03 15:12:28  nobody
  23.  * reimport after crash from backup
  24.  *
  25.  * Revision 1.1.1.1  1998/02/13  20:20:40  hfrieden
  26.  * Initial Import
  27.  *
  28.  * Revision 2.3  1995/05/26  16:16:23  john
  29.  * Split SATURN into define's for requiring cd, using cd, etc.
  30.  * Also started adding all the Rockwell stuff.
  31.  * 
  32.  * Revision 2.2  1995/03/24  17:48:21  john
  33.  * Made player files from saturn excrement the highest level for
  34.  * normal descent levels.
  35.  * 
  36.  * Revision 2.1  1995/03/21  14:38:49  john
  37.  * Ifdef'd out the NETWORK code.
  38.  * 
  39.  * Revision 2.0  1995/02/27  11:27:59  john
  40.  * New version 2.0, which has no anonymous unions, builds with
  41.  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
  42.  * 
  43.  * Revision 1.57  1995/02/13  20:34:55  john
  44.  * Lintized
  45.  * 
  46.  * Revision 1.56  1995/02/13  13:23:24  john
  47.  * Fixed bug with new player joystick selection.
  48.  * 
  49.  * Revision 1.55  1995/02/13  12:01:19  john
  50.  * Fixed bug with joystick throttle still asking for 
  51.  * calibration with new pilots.
  52.  * 
  53.  * Revision 1.54  1995/02/13  10:29:12  john
  54.  * Fixed bug with creating new player not resetting everything to default.
  55.  * 
  56.  * Revision 1.53  1995/02/03  10:58:46  john
  57.  * Added code to save shareware style saved games into new format...
  58.  * Also, made new player file format not have the saved game array in it.
  59.  * 
  60.  * Revision 1.52  1995/02/02  21:09:28  matt
  61.  * Let player start of level 8 if he made it to level 7 in the shareware
  62.  * 
  63.  * Revision 1.51  1995/02/02  18:50:14  john
  64.  * Added warning for FCS when new pilot chooses.
  65.  * 
  66.  * Revision 1.50  1995/02/02  11:21:34  john
  67.  * Made joystick calibrate when new user selects.
  68.  * 
  69.  * Revision 1.49  1995/02/01  18:06:38  rob
  70.  * Put defaults macros into descent.tex
  71.  * 
  72.  * Revision 1.48  1995/01/25  14:37:53  john
  73.  * Made joystick only prompt for calibration once...
  74.  * 
  75.  * Revision 1.47  1995/01/24  19:37:12  matt
  76.  * Took out incorrect mprintf
  77.  * 
  78.  * Revision 1.46  1995/01/22  18:57:22  matt
  79.  * Made player highest level work with missions
  80.  * 
  81.  * Revision 1.45  1995/01/21  16:36:05  matt
  82.  * Made starting level system work for now, pending integration with
  83.  * mission code.
  84.  * 
  85.  * Revision 1.44  1995/01/20  22:47:32  matt
  86.  * Mission system implemented, though imcompletely
  87.  * 
  88.  * Revision 1.43  1995/01/04  14:58:39  rob
  89.  * Fixed for shareware build.
  90.  * 
  91.  * Revision 1.42  1995/01/04  11:36:43  rob
  92.  * Added compatibility with older shareware pilot files.
  93.  * 
  94.  * Revision 1.41  1995/01/03  11:01:58  rob
  95.  * fixed a default macro.
  96.  * 
  97.  * Revision 1.40  1995/01/03  10:44:06  rob
  98.  * Added default taunt macros.
  99.  * 
  100.  * Revision 1.39  1994/12/13  10:01:16  allender
  101.  * pop up message box when unable to correctly save player file
  102.  * 
  103.  * Revision 1.38  1994/12/12  11:37:14  matt
  104.  * Fixed auto leveling defaults & saving
  105.  * 
  106.  * Revision 1.37  1994/12/12  00:26:59  matt
  107.  * Added support for no-levelling option
  108.  * 
  109.  * Revision 1.36  1994/12/10  19:09:54  matt
  110.  * Added assert for valid player number when loading game
  111.  * 
  112.  * Revision 1.35  1994/12/08  10:53:07  rob
  113.  * Fixed a bug in highest_level tracking.
  114.  * 
  115.  * Revision 1.34  1994/12/08  10:01:36  john
  116.  * Changed the way the player callsign stuff works.
  117.  * 
  118.  * Revision 1.33  1994/12/07  18:30:38  rob
  119.  * Load highest level along with player (used to be only if higher)
  120.  * Capped at LAST_LEVEL in case a person loads a registered player in shareware.
  121.  * 
  122.  * Revision 1.32  1994/12/03  16:01:12  matt
  123.  * When player file has bad version, force player to choose another
  124.  * 
  125.  * Revision 1.31  1994/12/02  19:54:00  yuan
  126.  * Localization.
  127.  * 
  128.  * Revision 1.30  1994/12/02  11:01:36  yuan
  129.  * Localization.
  130.  * 
  131.  * Revision 1.29  1994/11/29  03:46:28  john
  132.  * Added joystick sensitivity; Added sound channels to detail menu.  Removed -maxchannels
  133.  * command line arg.
  134.  * 
  135.  * Revision 1.28  1994/11/29  01:10:23  john
  136.  * Took out code that allowed new players to
  137.  * configure keyboard.
  138.  * 
  139.  * Revision 1.27  1994/11/25  22:47:10  matt
  140.  * Made saved game descriptions longer
  141.  * 
  142.  * Revision 1.26  1994/11/22  12:10:42  rob
  143.  * Fixed file handle left open if player file versions don't
  144.  * match.
  145.  * 
  146.  * Revision 1.25  1994/11/21  19:35:30  john
  147.  * Replaced calls to joy_init with if (joy_present)
  148.  * 
  149.  * Revision 1.24  1994/11/21  17:29:34  matt
  150.  * Cleaned up sequencing & game saving for secret levels
  151.  * 
  152.  * Revision 1.23  1994/11/21  11:10:01  john
  153.  * Fixed bug with read-only .plr file making the config file 
  154.  * not update.
  155.  * 
  156.  * Revision 1.22  1994/11/20  19:03:08  john
  157.  * Fixed bug with if not having a joystick, default 
  158.  * player input device is cyberman.
  159.  * 
  160.  * Revision 1.21  1994/11/17  12:24:07  matt
  161.  * Made an array the right size, to fix error loading games
  162.  * 
  163.  * Revision 1.20  1994/11/14  17:52:54  allender
  164.  * add call to WriteConfigFile when player files gets written
  165.  * 
  166.  * Revision 1.19  1994/11/14  17:19:23  rob
  167.  * Removed gamma, joystick calibration, and sound settings from player file.
  168.  * Added default difficulty and multi macros.
  169.  * 
  170.  * Revision 1.18  1994/11/07  14:01:23  john
  171.  * Changed the gamma correction sequencing.
  172.  * 
  173.  * Revision 1.17  1994/11/05  17:22:49  john
  174.  * Fixed lots of sequencing problems with newdemo stuff.
  175.  * 
  176.  * Revision 1.16  1994/11/01  16:40:11  john
  177.  * Added Gamma correction.
  178.  * 
  179.  * Revision 1.15  1994/10/24  19:56:50  john
  180.  * Made the new user setup prompt for config options.
  181.  * 
  182.  * Revision 1.14  1994/10/24  17:44:21  john
  183.  * Added stereo channel reversing.
  184.  * 
  185.  * Revision 1.13  1994/10/24  16:05:12  matt
  186.  * Improved handling of player names that are the names of DOS devices
  187.  * 
  188.  * Revision 1.12  1994/10/22  00:08:51  matt
  189.  * Fixed up problems with bonus & game sequencing
  190.  * Player doesn't get credit for hostages unless he gets them out alive
  191.  * 
  192.  * Revision 1.11  1994/10/19  19:59:57  john
  193.  * Added bonus points at the end of level based on skill level.
  194.  * 
  195.  * Revision 1.10  1994/10/19  15:14:34  john
  196.  * Took % hits out of player structure, made %kills work properly.
  197.  * 
  198.  * Revision 1.9  1994/10/19  12:44:26  john
  199.  * Added hours field to player structure.
  200.  * 
  201.  * Revision 1.8  1994/10/17  17:24:34  john
  202.  * Added starting_level to player struct.
  203.  * 
  204.  * Revision 1.7  1994/10/17  13:07:15  john
  205.  * Moved the descent.cfg info into the player config file.
  206.  * 
  207.  * Revision 1.6  1994/10/09  14:54:31  matt
  208.  * Made player cockpit state & window size save/restore with saved games & automap
  209.  * 
  210.  * Revision 1.5  1994/10/08  23:08:09  matt
  211.  * Added error check & handling for game load/save disk io
  212.  * 
  213.  * Revision 1.4  1994/10/05  17:40:54  rob
  214.  * Bumped save_file_version to 5 due to change in player.h
  215.  * 
  216.  * Revision 1.3  1994/10/03  23:00:54  matt
  217.  * New file version for shorter callsigns
  218.  * 
  219.  * Revision 1.2  1994/09/28  17:25:05  matt
  220.  * Added first draft of game save/load system
  221.  * 
  222.  * Revision 1.1  1994/09/27  14:39:12  matt
  223.  * Initial revision
  224.  * 
  225.  * 
  226.  */
  227.  
  228. #pragma off (unreferenced)
  229. static char rcsid[] = "$Id: playsave.c,v 1.1.1.1 1998/03/03 15:12:28 nobody Exp $";
  230. #pragma on (unreferenced)
  231.  
  232. #include <stdio.h>
  233. #include <string.h>
  234. #include <errno.h>
  235.  
  236. #include "error.h"
  237.  
  238. #include "gameseq.h"
  239. #include "player.h"
  240. #include "playsave.h"
  241. #include "joy.h"
  242. #include "kconfig.h"
  243. #include "digi.h"
  244. #include "newmenu.h"
  245. #include "joydefs.h"
  246. #include "palette.h"
  247. #include "multi.h"
  248. #include "menu.h"
  249. #include "config.h"
  250. #include "text.h"
  251. #include "mono.h"
  252. #include "state.h"
  253. #include "byteswap.h"
  254.  
  255. #define SAVE_FILE_ID            'DPLR'
  256.  
  257. //this is for version 5 and below
  258. typedef struct save_info_v5 {
  259.     int id;
  260.     short   saved_game_version,player_struct_version;
  261.     int     highest_level;
  262.     int default_difficulty_level;
  263.     int default_leveling_on;
  264. } save_info_v5;
  265.  
  266. //this is for version 6 and above 
  267. typedef struct save_info {
  268.     int id;
  269.     short   saved_game_version,player_struct_version;
  270.     int n_highest_levels;               //how many highest levels are saved
  271.     int default_difficulty_level;
  272.     int default_leveling_on;
  273. } save_info;
  274.  
  275. typedef struct hli {
  276.     char    shortname[9];
  277.     ubyte   level_num;
  278. } hli;
  279.  
  280. int n_highest_levels;
  281.  
  282. hli highest_levels[MAX_MISSIONS];
  283.  
  284. #define SAVED_GAME_VERSION      7       //increment this every time saved_game struct changes
  285.  
  286. //version 5 -> 6: added new highest level information
  287. //version 6 -> 7: stripped out the old saved_game array.
  288.  
  289. //the shareware is level 4
  290.  
  291. #define COMPATIBLE_SAVED_GAME_VERSION       4
  292. #define COMPATIBLE_PLAYER_STRUCT_VERSION    16
  293.  
  294. typedef struct saved_game {
  295.     char        name[GAME_NAME_LEN+1];      //extra char for terminating zero
  296.     player  player;
  297.     int     difficulty_level;       //which level game is played at
  298.     int     primary_weapon;     //which weapon selected
  299.     int     secondary_weapon;       //which weapon selected
  300.     int     cockpit_mode;           //which cockpit mode selected
  301.     int     window_w,window_h;  //size of player's window
  302.     int     next_level_num;     //which level we're going to
  303.     int     auto_leveling_on;       //does player have autoleveling on?
  304. } saved_game;
  305.  
  306. saved_game saved_games[N_SAVE_SLOTS];
  307.  
  308. int Default_leveling_on=1;
  309.  
  310. void init_game_list()
  311. {
  312.     int i;
  313.  
  314.     for (i=0;i<N_SAVE_SLOTS;i++)
  315.         saved_games[i].name[0] = 0;
  316. }
  317.  
  318. int new_player_config()
  319. {
  320.     int i,j,control_choice;
  321.     newmenu_item m[7];
  322.  
  323. RetrySelection:
  324.     for (i=0; i<CONTROL_MAX_TYPES; i++ )    {
  325.         m[i].type = NM_TYPE_MENU; m[i].text = CONTROL_TEXT(i);
  326.     }
  327.     m[0].text = TXT_CONTROL_KEYBOARD;
  328.  
  329.     control_choice = Config_control_type;               // Assume keyboard
  330.  
  331.     control_choice = newmenu_do1( NULL, TXT_CHOOSE_INPUT, CONTROL_MAX_TYPES, m, NULL, control_choice );
  332.  
  333.     if ( control_choice < 0 )
  334.         return 0;
  335.  
  336.     for (i=0;i<CONTROL_MAX_TYPES; i++ )
  337.         for (j=0;j<MAX_CONTROLS; j++ )
  338.             kconfig_settings[i][j] = default_kconfig_settings[i][j];
  339.     kc_set_controls();
  340.  
  341.     Config_control_type = control_choice;
  342.  
  343.     if ( Config_control_type==CONTROL_THRUSTMASTER_FCS) {
  344.         i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, TXT_FCS );
  345.         if (i==0) goto RetrySelection;
  346.     }
  347.     
  348.     if ( (Config_control_type>0) &&     (Config_control_type<5) )   {
  349.         joydefs_calibrate();
  350.     }
  351.  
  352.     Player_default_difficulty = 1;
  353.     Auto_leveling_on = Default_leveling_on = 1;
  354.     n_highest_levels = 1;
  355.     highest_levels[0].shortname[0] = 0;         //no name for mission 0
  356.     highest_levels[0].level_num = 1;                //was highest level in old struct
  357.     Config_joystick_sensitivity = 8;
  358.  
  359.     // Default taunt macros
  360.     #ifdef NETWORK
  361.     strcpy(Network_message_macro[0], TXT_DEF_MACRO_1);
  362.     strcpy(Network_message_macro[1], TXT_DEF_MACRO_2);
  363.     strcpy(Network_message_macro[2], TXT_DEF_MACRO_3);
  364.     strcpy(Network_message_macro[3], TXT_DEF_MACRO_4);
  365.     #endif
  366.  
  367.     return 1;
  368. }
  369.  
  370. //read in the player's saved games.  returns errno (0 == no error)
  371. int read_player_file()
  372. {
  373.     char filename[13];
  374.     FILE *file;
  375.     save_info info;
  376.     int errno_ret = 0;
  377.  
  378.     Assert(Player_num>=0 && Player_num<MAX_PLAYERS);
  379.  
  380.     //sprintf(filename,"%8s.plr",Players[Player_num].callsign);
  381.     sprintf(filename,"%s.plr",Players[Player_num].callsign);
  382.     file = fopen(filename,"rb");
  383.  
  384.     //check filename
  385.     if (file && isatty(fileno(file))) {
  386.         //if the callsign is the name of a tty device, prepend a char
  387.         fclose(file);
  388.         sprintf(filename,"$%.7s.plr",Players[Player_num].callsign);
  389.         file = fopen(filename,"rb");
  390.     }
  391.  
  392.     if (!file) {
  393.         return errno;
  394.     }
  395.  
  396.     if (fread(&info,sizeof(info),1,file) != 1) {
  397.         errno_ret = errno;
  398.         fclose(file);
  399.         return errno_ret;
  400.     }
  401.  
  402.     info.id = swapint(info.id);
  403.     info.saved_game_version = swapshort(info.saved_game_version);
  404.     info.player_struct_version = swapshort(info.player_struct_version);
  405.     info.n_highest_levels = swapint(info.n_highest_levels);
  406.     info.default_difficulty_level = swapint(info.default_difficulty_level);
  407.     info.default_leveling_on = swapint(info.default_leveling_on);
  408.  
  409.  
  410.     if (info.id!=SAVE_FILE_ID) {
  411.         nm_messagebox(TXT_ERROR, 1, TXT_OK, "Invalid player file");
  412.         fclose(file);
  413.         return -1;
  414.     }
  415.  
  416.     if (info.saved_game_version<COMPATIBLE_SAVED_GAME_VERSION || info.player_struct_version<COMPATIBLE_PLAYER_STRUCT_VERSION) {
  417.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_ERROR_PLR_VERSION);
  418.         fclose(file);
  419.         return -1;
  420.     }
  421.  
  422.     if (info.saved_game_version <= 5) {
  423.  
  424.         //deal with old-style highest level info
  425.  
  426.         n_highest_levels = 1;
  427.  
  428.         highest_levels[0].shortname[0] = 0;                         //no name for mission 0
  429.         highest_levels[0].level_num = info.n_highest_levels;    //was highest level in old struct
  430.  
  431.         //This hack allows the player to start on level 8 if he's made it to
  432.         //level 7 on the shareware.  We do this because the shareware didn't
  433.         //save the information that the player finished level 7, so the most
  434.         //we know is that he made it to level 7.
  435.         if (info.n_highest_levels==7)
  436.             highest_levels[0].level_num = 8;
  437.         
  438.     }
  439.     else {  //read new highest level info
  440.  
  441.         n_highest_levels = info.n_highest_levels;
  442.  
  443.         if (fread(highest_levels,sizeof(hli),n_highest_levels,file) != n_highest_levels) {
  444.             errno_ret = errno;
  445.             fclose(file);
  446.             return errno_ret;
  447.         }
  448.     }
  449.  
  450.     Player_default_difficulty = info.default_difficulty_level;
  451.     Default_leveling_on = info.default_leveling_on;
  452.  
  453.  
  454.     if ( info.saved_game_version < 7 )  {           // Read old saved games.
  455.         if (fread(saved_games,sizeof(saved_games),1,file) != 1) {
  456.             errno_ret = errno;
  457.             fclose(file);
  458.             return errno_ret;
  459.         }
  460.     }
  461.  
  462.     //read taunt macros
  463.     {
  464.         int i,len;
  465.  
  466.         len = (info.saved_game_version == 4)?SHAREWARE_MAX_MESSAGE_LEN:MAX_MESSAGE_LEN;
  467.  
  468.         #ifdef NETWORK
  469.         for (i = 0; i < 4; i++)
  470.             if (fread(Network_message_macro[i], len, 1, file) != 1)
  471.                 {errno_ret = errno; break;}
  472.         #else
  473.         i = 0;
  474.         fseek( file, 48*len, SEEK_CUR );
  475.         #endif
  476.     }
  477.  
  478.     //read kconfig data
  479.     {
  480.         if (fread( kconfig_settings, MAX_CONTROLS*CONTROL_MAX_TYPES, 1, file )!=1)
  481.             errno_ret=errno;
  482.         else if (fread(&Config_control_type, sizeof(ubyte), 1, file )!=1)
  483.             errno_ret=errno;
  484.         else if (fread(&Config_joystick_sensitivity, sizeof(ubyte), 1, file )!=1)
  485.             errno_ret=errno;
  486.  
  487.         if (errno_ret==0)   {
  488.             kc_set_controls();
  489.         }
  490.     }
  491.  
  492.     if (fclose(file) && errno_ret==0)
  493.         errno_ret = errno;
  494.  
  495.     if ( info.saved_game_version == COMPATIBLE_SAVED_GAME_VERSION )     {
  496.         int i;
  497.         
  498.         Assert( N_SAVE_SLOTS == 10 );
  499.  
  500.         for (i=0; i<N_SAVE_SLOTS; i++ ) {
  501.             if ( saved_games[i].name[0] )   {
  502.                 state_save_old_game(i, saved_games[i].name, &saved_games[i].player, 
  503.                     saved_games[i].difficulty_level, saved_games[i].primary_weapon, 
  504.                     saved_games[i].secondary_weapon, saved_games[i].next_level_num );
  505.             }
  506.         }
  507.         write_player_file();
  508.     }
  509.  
  510.     return errno_ret;
  511.  
  512. }
  513.  
  514. //finds entry for this level in table.  if not found, returns ptr to 
  515. //empty entry.  If no empty entries, takes over last one 
  516. int find_hli_entry()
  517. {
  518.     int i;
  519.  
  520.     for (i=0;i<n_highest_levels;i++)
  521.         if (!stricmp(highest_levels[i].shortname,Mission_list[Current_mission_num].filename))
  522.             break;
  523.  
  524.     if (i==n_highest_levels) {      //not found.  create entry
  525.  
  526.         if (i==MAX_MISSIONS)
  527.             i--;        //take last entry
  528.         else
  529.             n_highest_levels++;
  530.  
  531.         strcpy(highest_levels[i].shortname,Mission_list[Current_mission_num].filename);
  532.         highest_levels[i].level_num = 0;
  533.     }
  534.  
  535.     return i;
  536. }
  537.  
  538. //set a new highest level for player for this mission
  539. void set_highest_level(int levelnum)
  540. {
  541.     int ret,i;
  542.  
  543.     if ((ret=read_player_file()) != 0)
  544.         if (ret != ENOENT)      //if file doesn't exist, that's ok
  545.             return;
  546.  
  547.     i = find_hli_entry();
  548.  
  549.     if (levelnum > highest_levels[i].level_num)
  550.         highest_levels[i].level_num = levelnum;
  551.  
  552.     write_player_file();
  553. }
  554.  
  555. //gets the player's highest level from the file for this mission
  556. int get_highest_level(void)
  557. {
  558.     int i;
  559.     int highest_saturn_level = 0;
  560.     read_player_file();
  561. #ifndef DEST_SAT
  562.     if (strlen(Mission_list[Current_mission_num].filename)==0 ) {
  563.         for (i=0;i<n_highest_levels;i++)
  564.             if (!stricmp(highest_levels[i].shortname, "DESTSAT"))   //  Destination Saturn.
  565.                 highest_saturn_level = highest_levels[i].level_num; 
  566.     }
  567. #endif
  568.    i = highest_levels[find_hli_entry()].level_num;
  569.     if ( highest_saturn_level > i )
  570.     i = highest_saturn_level;
  571.     return i;
  572. }
  573.  
  574.  
  575. //write out player's saved games.  returns errno (0 == no error)
  576. int write_player_file()
  577. {
  578.     char filename[13];
  579.     FILE *file;
  580.     save_info info;
  581.     int errno_ret;
  582.  
  583.     errno_ret = WriteConfigFile();
  584.  
  585.     info.id = swapint(SAVE_FILE_ID);
  586.     info.saved_game_version = swapshort(SAVED_GAME_VERSION);
  587.     info.player_struct_version = swapshort(PLAYER_STRUCT_VERSION);
  588.     info.default_difficulty_level = swapint(Player_default_difficulty);
  589.     info.default_leveling_on = swapint(Auto_leveling_on);
  590.  
  591.     info.n_highest_levels = swapint(n_highest_levels);
  592.  
  593.     sprintf(filename,"%s.plr",Players[Player_num].callsign);
  594.     file = fopen(filename,"wb");
  595.  
  596.     //check filename
  597.     if (file && isatty(fileno(file))) {
  598.  
  599.         //if the callsign is the name of a tty device, prepend a char
  600.  
  601.         fclose(file);
  602.         sprintf(filename,"$%.7s.plr",Players[Player_num].callsign);
  603.         file = fopen(filename,"wb");
  604.     }
  605.     if (!file)
  606.         return errno;
  607.  
  608.     errno_ret = 0;
  609.  
  610.     if (fwrite(&info,sizeof(info),1,file) != 1) {
  611.         errno_ret = errno;
  612.         fclose(file);
  613.         return errno_ret;
  614.     }
  615.     //write higest level info
  616.     if ((fwrite(highest_levels, sizeof(hli), n_highest_levels, file) != n_highest_levels)) {
  617.         errno_ret = errno;
  618.         fclose(file);
  619.         return errno_ret;
  620.     }
  621.  
  622. //  if (fwrite(saved_games,sizeof(saved_games),1,file) != 1) {
  623. //      errno_ret = errno;
  624. //      fclose(file);
  625. //      return errno_ret;
  626. //  }
  627.  
  628.     #ifdef NETWORK
  629.     if ((fwrite(Network_message_macro, MAX_MESSAGE_LEN, 4, file) != 4)) {
  630.         errno_ret = errno;
  631.         fclose(file);
  632.         return errno_ret;
  633.     }
  634.     #else
  635.     fseek( file, MAX_MESSAGE_LEN * 4, SEEK_CUR );
  636.     #endif
  637.  
  638.     //write kconfig info
  639.     {
  640.         if (fwrite( kconfig_settings, MAX_CONTROLS*CONTROL_MAX_TYPES, 1, file )!=1)
  641.             errno_ret=errno;
  642.         else if (fwrite( &Config_control_type, sizeof(ubyte), 1, file )!=1)
  643.             errno_ret=errno;
  644.         else if (fwrite( &Config_joystick_sensitivity, sizeof(ubyte), 1, file )!=1)
  645.             errno_ret=errno;
  646.     }
  647.  
  648.     if (fclose(file))
  649.         errno_ret = errno;
  650.  
  651.     if (errno_ret != 0) {
  652.         remove(filename);           //delete bogus file
  653.         nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s\n\n%s",TXT_ERROR_WRITING_PLR, strerror(errno_ret));
  654.     }
  655.         
  656.  
  657.     return errno_ret;
  658.  
  659. }
  660.  
  661. //returns errno (0 == no error)
  662. int save_player_game(int slot_num,char *text)
  663. {
  664.     int ret;
  665.  
  666.     if ((ret=read_player_file()) != 0)
  667.         if (ret != ENOENT)      //if file doesn't exist, that's ok
  668.             return ret;
  669.  
  670.     Assert(slot_num < N_SAVE_SLOTS);
  671.  
  672.     strcpy(saved_games[slot_num].name,text);
  673.  
  674.     saved_games[slot_num].player = Players[Player_num];
  675.  
  676.     saved_games[slot_num].difficulty_level  = Difficulty_level;
  677.     saved_games[slot_num].auto_leveling_on  = Auto_leveling_on;
  678.     saved_games[slot_num].primary_weapon    = Primary_weapon;
  679.     saved_games[slot_num].secondary_weapon  = Secondary_weapon;
  680.     saved_games[slot_num].cockpit_mode      = Cockpit_mode;
  681.     saved_games[slot_num].window_w          = Game_window_w;
  682.     saved_games[slot_num].window_h          = Game_window_h;
  683.     saved_games[slot_num].next_level_num    = Next_level_num;
  684.  
  685.     return write_player_file();
  686. }
  687.  
  688.  
  689. //returns errno (0 == no error)
  690. int load_player_game(int slot_num)
  691. {
  692.     char save_callsign[CALLSIGN_LEN+1];
  693.     int ret;
  694.  
  695.     Assert(slot_num < N_SAVE_SLOTS);
  696.  
  697.     if ((ret=read_player_file()) != 0)
  698.         return ret;
  699.  
  700.     Assert(saved_games[slot_num].name[0] != 0);
  701.  
  702.     strcpy(save_callsign,Players[Player_num].callsign);
  703.     Players[Player_num] = saved_games[slot_num].player;
  704.     strcpy(Players[Player_num].callsign,save_callsign);
  705.  
  706.     Difficulty_level    = saved_games[slot_num].difficulty_level;
  707.     Auto_leveling_on    = saved_games[slot_num].auto_leveling_on;
  708.     Primary_weapon      = saved_games[slot_num].primary_weapon;
  709.     Secondary_weapon    = saved_games[slot_num].secondary_weapon;
  710.     Cockpit_mode        = saved_games[slot_num].cockpit_mode;
  711.     Game_window_w       = saved_games[slot_num].window_w;
  712.     Game_window_h       = saved_games[slot_num].window_h;
  713.  
  714.     Players[Player_num].level = saved_games[slot_num].next_level_num;
  715.  
  716.     return 0;
  717. }
  718.  
  719. //fills in a list of pointers to strings describing saved games
  720. //returns the number of non-empty slots
  721. //returns -1 if this is a new player
  722. int get_game_list(char *game_text[N_SAVE_SLOTS])
  723. {
  724.     int i,count,ret;
  725.  
  726.     ret = read_player_file();
  727.  
  728.     for (i=count=0;i<N_SAVE_SLOTS;i++) {
  729.         if (game_text)
  730.             game_text[i] = saved_games[i].name;
  731.  
  732.         if (saved_games[i].name[0])
  733.             count++;
  734.     }
  735.  
  736.     return (ret==0)?count:-1;       //-1 means new file was created
  737.  
  738. }
  739.  
  740. //update the player's highest level.  returns errno (0 == no error)
  741. int update_player_file()
  742. {
  743.     int ret;
  744.  
  745.     if ((ret=read_player_file()) != 0)
  746.         if (ret != ENOENT)      //if file doesn't exist, that's ok
  747.             return ret;
  748.  
  749.     return write_player_file();
  750. }
  751.